package com.jincheng.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jincheng.common.constant.Constants;
import com.jincheng.common.core.domain.AjaxResult;
import com.jincheng.common.core.domain.entity.SysDept;
import com.jincheng.common.core.domain.entity.SysDictData;
import com.jincheng.common.core.domain.entity.SysUser;
import com.jincheng.common.core.domain.model.LoginUser;
import com.jincheng.common.core.redis.RedisCache;
import com.jincheng.common.exception.ServiceException;
import com.jincheng.common.utils.DateUtils;
import com.jincheng.common.utils.DictUtils;
import com.jincheng.system.domain.Activity;
import com.jincheng.system.domain.SignInSheet;
import com.jincheng.system.domain.Tribe;
import com.jincheng.system.domain.dto.*;
import com.jincheng.system.domain.vo.HeatMapVo;
import com.jincheng.system.domain.vo.UserActivityVo;
import com.jincheng.system.mapper.ActivityMapper;
import com.jincheng.system.mapper.SignInSheetMapper;
import com.jincheng.system.mapper.SysUserMapper;
import com.jincheng.system.mapper.TribeMapper;
import com.jincheng.system.service.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import static com.jincheng.common.constant.CacheConstants.QR_CODE_KEY;
import static com.jincheng.common.core.domain.AjaxResult.error;
import static com.jincheng.common.core.domain.AjaxResult.success;
import static com.jincheng.common.utils.DateUtils.getNowDate;
import static com.jincheng.common.utils.SecurityUtils.*;

/**
 * 活动Service业务层处理
 *
 * @author DairyJay
 * @date 2024-12-21
 */
@RequiredArgsConstructor
@Service
public class ActivityServiceImpl implements IActivityService {
    private final ActivityMapper activityMapper;
    private final SysUserMapper userMapper;
    private final ISignInSheetService signInSheetService;
    private final ISysDeptService deptService;
    private final ITribeService tribeService;
    private final TribeMapper tribeMapper;
    private final SignInSheetMapper signInSheetMapper;
    private final ISysNoticeService noticeService;
    private final RedisCache redisCache;


    /**
     * 查询活动
     *
     * @param activityId 活动主键
     * @return 活动
     */
    @Override
    public Activity selectActivityByActivityId(Long activityId) {
        return activityMapper.selectActivityByActivityId(activityId);
    }

    @Override
    public Activity selectActivityByActivityIds(List<Long> activityIds) {
        return null;
    }

    /**
     * 查询活动列表
     *
     * @param activity 活动
     * @return 活动
     */
    @Override
    public List<Activity> selectActivityList(Activity activity) {
        return activityMapper.selectActivityList(activity);
    }

    /**
     * 查询活动列表
     *
     * @return 活动
     */
    @Override
    public List<ActivityDetailDTO> selectSimpleAll() {
        return activityMapper.selectActivitySimpleList(new ActivityScreenDTO());
    }


    /**
     * 查询活动列表
     *
     * @param activityScreenDTO 活动筛选信息
     * @return 活动集合
     */
    @Override
    public List<ActivityDetailDTO> selectActivitySimpleList(ActivityScreenDTO activityScreenDTO) {
        return activityMapper.selectActivitySimpleList(activityScreenDTO);
    }

    /**
     * 查询活动列表
     *
     * @param auditStatus 审核状态
     * @return 活动集合
     */
    @Override
    public List<ActivitySimpleDTO> selectActivityListByUser(Long userId, Integer auditStatus) {
        return activityMapper.selectActivityListByUser(userId, auditStatus, null);
    }

    /**
     * 查询用户积分总和
     *
     * @param userId 用户id
     * @return 积分总和
     */
    @Override
    public Long selectIntegralSumByUserId(Long userId) {
        return activityMapper.selectIntegralSumByUserId(userId);
    }

    /**
     * 新增活动
     *
     * @param activity 活动
     * @return 结果
     */
    @Override
    public int insertActivity(Activity activity) {
        activity.setCreateTime(getNowDate());
        return activityMapper.insertActivity(activity);
    }

    /**
     * 修改活动
     *
     * @param activity 活动
     * @return 结果
     */
    @Override
    public int updateActivity(Activity activity) {
//        Tribe tribe = tribeMapper.selectTribeByTribeId(activity.getTribeId());
        Tribe tribe = tribeMapper.selectById(activity.getTribeId());
        if (!getLoginUser().getUser().isAdmin() && !Objects.equals(tribe.getChairId(), getUserId()))
            throw new RuntimeException("只有能修改自己负责的活动！");
        activity.setUpdateTime(getNowDate());
        activity.setUpdateBy(getUsername());
        activity.setUpdateBy(userMapper.selectUserById(activity.getTribeId()).getNickName());
        return activityMapper.updateActivity(activity);
    }

    /**
     * 批量删除活动
     *
     * @param activityIds 需要删除的活动主键
     * @return 结果
     */
    @Override
    public int deleteActivityByActivityIds(Long[] activityIds) {
        return activityMapper.deleteActivityByActivityIds(activityIds);
    }

    /**
     * 删除活动信息
     *
     * @param activityId 活动主键
     * @return 结果
     */
    @Override
    public int deleteActivityByActivityId(Long activityId) {
        return activityMapper.deleteActivityByActivityId(activityId);
    }


    /**
     * 根据筛选条件查询活动列表
     *
     * @param activityScreenDTO 数据查询DTO
     * @return 活动列表
     */
    @Override
    public List<ActivityDetailDTO> selectActivityListByScreen(ActivityScreenDTO activityScreenDTO) {
        return activityMapper.selectActivityListByScreenPage(activityScreenDTO);
    }

    /**
     * 查询活动详情
     *
     * @param activityId 活动id
     * @return 活动详情
     */
    @Override
    public Map<String, Object> selectActivityDetail(Long activityId) {
        Map<String, Object> data = new HashMap<>();

        // 获取活动详情并设置字典标签
        Activity activity = selectActivityByActivityId(activityId);
        if (activity == null) {
            return data; // 返回空数据或抛出异常，视业务需求而定
        }
        setActivityDictLabels(activity);
        data.put("activity", activity);

        // 获取部落信息
        Tribe tribe = tribeService.selectTribeByTribeId(activity.getTribeId());
        if (tribe != null) {
            SysDept sysDept = deptService.selectDeptById(tribe.getDeptId());
            Map<String, Object> tribeInfo = buildTribeInfo(tribe, sysDept);
            data.put("tribe", tribeInfo);
        }

        LambdaQueryWrapper<SignInSheet> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(SignInSheet::getActivityId, activityId)
                .eq(SignInSheet::getUserId, getUserId());
        SignInSheet sheet = signInSheetMapper.selectOne(queryWrapper);
        int auditStatus = -1;
        if (sheet != null) {
            auditStatus = sheet.getAuditStatus();
        }
        data.put("auditStatus", auditStatus);
        return data;
    }

    /**
     * 设置活动字典标签
     *
     * @param activity 活动对象
     */
    private void setActivityDictLabels(Activity activity) {
        String levelName = DictUtils.getDictLabel("activity_level", activity.getLevel().toString());
        activity.setLevelName(levelName);
        String participationObjectName = DictUtils.getDictLabel("activity_object", activity.getParticipationObject().toString());
        activity.setParticipationObjectName(participationObjectName);
        String signMethodName = DictUtils.getDictLabel("activity_sign_method", activity.getSignMethod().toString());
        activity.setSignMethodName(signMethodName);
    }

    /**
     * 构建部落信息
     *
     * @param tribe   部落对象
     * @param sysDept 部门对象
     * @return 部落信息Map
     */
    private Map<String, Object> buildTribeInfo(Tribe tribe, SysDept sysDept) {
        Map<String, Object> tribeInfo = new HashMap<>();
        tribeInfo.put("tribeId", tribe.getTribeId());
        tribeInfo.put("chairId", tribe.getChairId());
        tribeInfo.put("chairName", tribe.getChairName());
        tribeInfo.put("chairPhone", tribe.getChairPhone());
        tribeInfo.put("deptName", sysDept != null ? sysDept.getDeptName() : null);
        tribeInfo.put("tribeName", tribe.getTribeName());
        return tribeInfo;
    }

    /**
     * 辅助方法：创建签到人数条目
     *
     * @param title  标题
     * @param number 数量
     * @return 包含标题和数量的 Map
     */
    private Map<String, Object> createSignNumEntry(String title, long number) {
        Map<String, Object> entry = new HashMap<>();
        entry.put("title", title);
        entry.put("number", number);
        return entry;
    }


    /**
     * 报名活动
     *
     * @param activityId 活动id
     * @param loginUser  当前登录用户信息
     * @return 是否报名成功
     */
    @Override
    public AjaxResult apply(Long activityId, LoginUser loginUser) {
        // 校验活动是否存在
        Activity activity = activityMapper.selectActivityByActivityId(activityId);
        if (activity == null) {
            return error("活动不存在");
        }

        // 校验用户信息
        Long userId = loginUser.getUserId();
        SysUser user = loginUser.getUser();
        if (userId == null || user == null) {
            return error("用户未登录或信息不完整");
        }

        // 检查是否已报名
        if (signInSheetService.checkWhetherApply(activityId, userId)) {
            return error("已报名");
        }

        // 校验用户是否有权限参与活动
        if (!checkParticipationPermission(activity, user)) {
            return error("不符合参与条件");
        }

        // 校验报名时间
        Date now = new Date();
        if (now.before(activity.getApplyStartTime()) || now.after(activity.getApplyEndTime())) {
            return error("不在报名时间内");
        }

        // 校验人数限制
        SignInSheet signInSheet = new SignInSheet();
        signInSheet.setActivityId(activityId);
        signInSheet.setAuditStatus(0);
        List<SignInSheet> signInSheetList = signInSheetService.selectList(signInSheet);
        if (activity.getQuantity() != null && signInSheetList.size() >= activity.getQuantity()) {
            return error("名额已满");
        }


        int auditStatus = activity.getIsCheck() ? 2 : 0;

        // 保存报名记录
        if (!signInSheetService.insertApplyInfo(activityId, userId, auditStatus)) {
            return error("报名失败");
        }
        // 发送通知
        if (activity.getIsCheck()) {
            noticeService.sendActivityApplyNotice(activity);
        }

        return success(activity.getIsCheck() ? "已申请，等待审核" : "报名成功");
    }

    /**
     * 校验用户是否符合参与条件
     *
     * @param activity 活动信息
     * @param user     用户信息
     * @return 是否符合参与条件
     */
    private boolean checkParticipationPermission(Activity activity, SysUser user) {
        if (activity.getParticipationObject() == 1) { // 按院系参与
            // 校验院系是否匹配
            for (Long deptId : activity.getDeptList()) {
                if (!deptService.checkDeptChildByDeptId(deptId, user.getDeptId())) {
                    return false;
                }
            }
//            Integer participationObject = activity.getParticipationObject();
//            String activityObject = DictUtils.getDictLabel("activity_object", participationObject + "");
            // 校验年级是否在允许范围内
            String[] allowedGradeIds = activity.getGrade().split(",");
            Integer userGrade = user.getGrade();
            return Arrays.stream(allowedGradeIds)
                    .map(Integer::parseInt)
                    .anyMatch(gradeId -> gradeId.equals(userGrade));
        } else if (activity.getParticipationObject() == 2) { // 按部落参与
            return tribeService.checkUserInTribe(user.getUserId(), activity.getTribeId());
        }
        return false; // 默认情况下不允许报名
    }


    /**
     * 根据用户id查询活动列表
     *
     * @return 活动列表
     */
    @Override
    public List<ActivitySimpleDTO> selectActivityListByUserId() {
        Long userId = getUserId();
        return activityMapper.selectActivityListByUserId(userId);
    }

    /**
     * 获取活动统计信息
     *
     * @return 活动统计信息
     */
    @Override
    public ActivityStatisticsDTO getStatistics() {
        // 获取用户总数
        long userCount = userMapper.selectCount();

        // 获取活动总数
        long totalActivities = activityMapper.selectCount(new QueryWrapper<>());

        // 计算每个用户的平均活动数
        double avgActivitiesPerUser = userCount == 0 ? 0 : (double) totalActivities / userCount;

        // 获取参与活动的记录数（audit_status = 0）
        long participate = signInSheetMapper.selectCount(new LambdaQueryWrapper<SignInSheet>()
                .eq(SignInSheet::getAuditStatus, 0));

        // 获取已签到的记录数（audit_status = 0 且 sign_in_time 不为 null）
        long signedIn = signInSheetMapper.selectCount(new LambdaQueryWrapper<SignInSheet>()
                .eq(SignInSheet::getAuditStatus, 0)
                .isNotNull(SignInSheet::getSignInTime));

        // 计算签到率（已签到 / 应签到）
        double signInRate = participate == 0 ? 0 : (double) signedIn / participate;

        // 获取需要签退的记录数（audit_status = 1 且 sign_out_time 不为 null）
        long needSignOutCount = signInSheetMapper.selectCount(new LambdaQueryWrapper<SignInSheet>()
                .eq(SignInSheet::getAuditStatus, 1)
                .isNotNull(SignInSheet::getSignOutTime));

        // 获取已签退的记录数（audit_status = 1 且 sign_out_time 不为 null 且 sign_out_time < create_time）
        long signedOut = signInSheetMapper.selectCount(new LambdaQueryWrapper<SignInSheet>()
                .eq(SignInSheet::getAuditStatus, 1)
                .isNotNull(SignInSheet::getSignOutTime)
                .apply("sign_out_time > create_time"));

        // 计算签退率（已签退 / 应签退）
        double signOutRate = needSignOutCount == 0 ? 0 : (double) signedOut / needSignOutCount;

        return new ActivityStatisticsDTO(totalActivities, avgActivitiesPerUser, participate, signInRate, signOutRate);
    }

    /**
     * 获取活动报名热力图
     *
     * @param year 年份
     * @return 热力图数据
     */
    @Override
    public List<HeatMapVo> getHeatMap(Integer year) {
        return activityMapper.getHeatMapByYear(year);
    }

    /**
     * 获取活动报名饼图数据
     *
     * @param type 类型（"dept" 或 "level"）
     * @return 饼图数据列表
     */
    @Override
    public List<PieChartDTO> getPie(String type) {
        // 校验 type 参数
        if (type == null || (!type.equals("dept") && !type.equals("level"))) {
            throw new IllegalArgumentException("Invalid type: " + type);
        }

        // 从数据库查询饼图数据
        Map<String, Map<String, Long>> pieData = activityMapper.getPie(type);

        // 从 Redis 获取字典数据
        List<SysDictData> dictList = getDictList(type);

        // 构建并返回饼图数据
        return PieChartDTO.buildPieData(pieData, dictList);
    }

    /**
     * 根据 type 获取字典列表
     *
     * @param type 类型
     * @return 字典数据列表
     */
    private List<SysDictData> getDictList(String type) {
        String dictType = type.equals("dept") ? "sys_dept" : "activity_level";
        return DictUtils.getDictCache(dictType);
    }

    /**
     * 获取用户端的活动统计信息
     *
     * @return 活动统计信息
     */
    @Override
    public UserActivityVo getUserStatistics() {
        int total = 0, signIn = 0, signOut = 0, needSignOut = 0;
        double signInRate = 0.0, signOutRate = 0.0;

        LambdaQueryWrapper<SignInSheet> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SignInSheet::getUserId, getUserId())
                .eq(SignInSheet::getAuditStatus, 0);
        List<SignInSheet> signInSheets = signInSheetMapper.selectList(queryWrapper);

        if (signInSheets != null) {
            // 活动不为空则构造数据
            total = signInSheets.size();
            for (SignInSheet sis : signInSheets) {
                // 签到时间不为空则签到数+1
                if (sis.getSignInTime() != null) ++signIn;
                if (sis.getSignOutTime() != null) {
                    // 签退时间不为空则需要签退数+1
                    ++needSignOut;
                    if (sis.getCreateTime().before(sis.getSignOutTime()))
                        // 签退时间小于创建时间则未签退
                        ++signOut;
                }
            }
            // 计算签到率和签退率
            signInRate = total != 0 ? signIn * 1.0 / total : 0;
            signOutRate = needSignOut != 0 ? signOut * 1.0 / needSignOut : 0;
        }

        return UserActivityVo.builder()
                .total(total)
                .signIn(signIn)
                .signInRate(signInRate)
                .signOut(signOut)
                .signOutRate(signOutRate)
                .build();
    }

    /**
     * 根据年份获取当前用户参与的活动信息
     *
     * @param year 年份，0则为一年内
     * @return 日历热力图数据
     */
    @Override
    public Map<String, List<HeatMapVo>> getUserHeatMap(Integer year) {
        List<HeatMapVo> heatMapVoList = signInSheetMapper.getUserHeatMap(getUserId(), year);

        // 使用 TreeMap 保证月份顺序
        return heatMapVoList.stream()
                .collect(Collectors.groupingBy(
                        vo -> vo.getDate().substring(0, 7),
                        TreeMap::new, // 使用 TreeMap 保证顺序
                        Collectors.toList()
                ));
    }

    /**
     * 根据用户id和活动状态查询活动列表
     *
     * @param userId         用户id
     * @param activityStatus 活动状态
     * @return 活动列表
     */
    @Override
    public List<ActivitySimpleDTO> selectActivityListByUserAndActivityStatus(Long userId, Integer activityStatus) {
        return activityMapper.selectActivityListByUser(userId, null, activityStatus);
    }

    /**
     * 活动扫码签到/签退
     *
     * @param verifyKey 二维码编号
     * @param userId    用户id
     * @return 签到/签退结果
     */
    @Override
    public String signInOrOutByQrCode(String verifyKey, Long userId) {
        Map<String, Object> cacheObject = redisCache.getCacheObject(QR_CODE_KEY + verifyKey);
        if (cacheObject == null){
           throw new ServiceException("验证码已过期");
        }
        Long activityId = (Long) cacheObject.get("activityId");
        Boolean isSignIn = (Boolean) cacheObject.get("isSignInCode");

        return signInOrOut(userId, isSignIn, activityId);
    }


    /**
     * 活动定位签到/签退
     *
     * @param userId     用户id
     * @param isSignIn   是否是签到
     * @param activityId 活动id
     * @return 签到/签退结果
     */
    @Override
    public String signInOrOut(Long userId, Boolean isSignIn, Long activityId) {
        String msg = isSignIn ? "签到" : "签退";
        SignInSheet sis = signInSheetMapper.selectOne(new LambdaQueryWrapper<SignInSheet>()
                .eq(SignInSheet::getActivityId, activityId)
                .eq(SignInSheet::getUserId, userId)
        );

        // 判断是否报名
        if (sis == null) {
            throw new ServiceException("您未报名该活动,无法" + msg);
        }

        Activity activity = selectActivityByActivityId(activityId);
        Date currentTime = getNowDate();

        LambdaUpdateWrapper<SignInSheet> updateWrapper = new LambdaUpdateWrapper<SignInSheet>()
                .eq(SignInSheet::getActivityId, activityId)
                .eq(SignInSheet::getUserId, userId);

        if (isSignIn) {// 签到
            // 判断是否在签到时间范围内
            if (currentTime.before(activity.getStartTime()) || activity.getEndTime().before(currentTime)){
                throw new ServiceException("不在签到时间内");
            }
            if (sis.getSignInTime() != null) {
                throw new ServiceException("您已签到,请勿重复签到");
            }

            // 进行签到
            updateWrapper.set(SignInSheet::getSignInTime, currentTime);
            int affectedRows = signInSheetMapper.update(updateWrapper);
            if (affectedRows == 0)
                throw new ServiceException("签到失败!");
        } else { // 签退
            // 判断是否在签退时间范围内
            if (currentTime.before(activity.getStartTime()) || activity.getEndTime().before(currentTime)){
                throw new ServiceException("不在签退时间内");
            }
            // 判断是否签到
            if (sis.getSignInTime() == null) {
                throw new ServiceException("您还未签到,不能直接签退");
            }
            // 判断是否已经签退
            if (sis.getSignOutTime().after(sis.getCreateTime())) {
                throw new ServiceException("您已签退,请勿重复签退");
            }
            // 进行签退
            updateWrapper.set(SignInSheet::getSignOutTime, currentTime);
            int affectedRows = signInSheetMapper.update(updateWrapper);
            if (affectedRows == 0)
                throw new ServiceException("签退失败!");
        }

        return msg + "成功!";
    }
}
